﻿Imports System

Namespace System.Workflow.ComponentModel
    Friend NotInheritable Class FaultAndCancellationHandlingFilter
        Inherits ActivityExecutionFilter
        Implements IActivityEventListener([Of] ActivityExecutionStatusChangedEventArgs)
        ' Methods
        Public Overrides Function Execute(ByVal activity As Activity, ByVal executionContext As ActivityExecutionContext) As ActivityExecutionStatus
            If (activity Is Nothing) Then
                Throw New ArgumentNullException("activity")
            End If
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            If Not TypeOf activity Is CompositeActivity Then
                Throw New InvalidOperationException("activity")
            End If
            executionContext.Activity.HoldLockOnStatusChange(Me)
            Return MyBase.Execute(activity, executionContext)
        End Function

        Friend Shared Function GetCancellationHandler(ByVal activityWithCancelHandler As Activity) As Activity
            Dim activity2 As CompositeActivity = [TryCast](activityWithCancelHandler,CompositeActivity)
            If (Not activity2 Is Nothing) Then
                Dim activity3 As Activity
                For Each activity3 In DirectCast(activity2, ISupportAlternateFlow).AlternateFlowActivities
                    If TypeOf activity3 Is CancellationHandlerActivity Then
                        Return activity3
                    End If
                Next
            End If
            Return Nothing
        End Function

        Friend Shared Function GetFaultHandlers(ByVal activityWithExceptionHandlers As Activity) As CompositeActivity
            Dim activity2 As CompositeActivity = [TryCast](activityWithExceptionHandlers,CompositeActivity)
            If (Not activity2 Is Nothing) Then
                Dim activity3 As Activity
                For Each activity3 In DirectCast(activity2, ISupportAlternateFlow).AlternateFlowActivities
                    If TypeOf activity3 Is FaultHandlersActivity Then
                        Return [TryCast](activity3,CompositeActivity)
                    End If
                Next
            End If
            Return Nothing
        End Function

        Public Overrides Function HandleFault(ByVal activity As Activity, ByVal executionContext As ActivityExecutionContext, ByVal exception As Exception) As ActivityExecutionStatus
            If activity.HasPrimaryClosed Then
                Dim faultHandlers As Activity = FaultAndCancellationHandlingFilter.GetFaultHandlers(executionContext.Activity)
                If (((Not faultHandlers Is Nothing) AndAlso (faultHandlers.ExecutionStatus <> ActivityExecutionStatus.Closed)) AndAlso (faultHandlers.ExecutionStatus <> ActivityExecutionStatus.Initialized)) Then
                    If (faultHandlers.ExecutionStatus = ActivityExecutionStatus.Executing) Then
                        executionContext.CancelActivity(faultHandlers)
                    End If
                    Return ActivityExecutionStatus.Faulting
                End If
                faultHandlers = FaultAndCancellationHandlingFilter.GetCancellationHandler(executionContext.Activity)
                If (((Not faultHandlers Is Nothing) AndAlso (faultHandlers.ExecutionStatus <> ActivityExecutionStatus.Closed)) AndAlso (faultHandlers.ExecutionStatus <> ActivityExecutionStatus.Initialized)) Then
                    If (faultHandlers.ExecutionStatus = ActivityExecutionStatus.Executing) Then
                        executionContext.CancelActivity(faultHandlers)
                    End If
                    Return ActivityExecutionStatus.Faulting
                End If
                If CBool(activity.GetValue(FaultAndCancellationHandlingFilter.FaultProcessedProperty)) Then
                    Me.SafeReleaseLockOnStatusChange(executionContext)
                End If
            End If
            Return MyBase.HandleFault(activity, executionContext, exception)
        End Function

        Public Sub OnEvent(ByVal sender As Object, ByVal e As ActivityExecutionStatusChangedEventArgs)
            Dim context As ActivityExecutionContext = [TryCast](sender,ActivityExecutionContext)
            If (context Is Nothing) Then
                Throw New ArgumentException("sender")
            End If
            If (e.Activity Is context.Activity) Then
                If (context.Activity.HasPrimaryClosed AndAlso Not CBool(context.Activity.GetValue(FaultAndCancellationHandlingFilter.FaultProcessedProperty))) Then
                    context.Activity.SetValue(FaultAndCancellationHandlingFilter.FaultProcessedProperty, True)
                    If ((Not context.Activity.WasExecuting OrElse (context.Activity.ExecutionResult <> ActivityExecutionResult.Faulted)) OrElse (context.Activity.GetValue(ActivityExecutionContext.CurrentExceptionProperty) Is Nothing)) Then
                        If (context.Activity.ExecutionResult <> ActivityExecutionResult.Canceled) Then
                            Me.SafeReleaseLockOnStatusChange(context)
                        Else
                            Dim cancellationHandler As Activity = FaultAndCancellationHandlingFilter.GetCancellationHandler(context.Activity)
                            If (Not cancellationHandler Is Nothing) Then
                                cancellationHandler.RegisterForStatusChange(Activity.ClosedEvent, Me)
                                context.ExecuteActivity(cancellationHandler)
                            ElseIf Not CompensationUtils.TryCompensateLastCompletedChildActivity(context, context.Activity, Me) Then
                                Me.SafeReleaseLockOnStatusChange(context)
                            End If
                        End If
                    Else
                        Dim faultHandlers As CompositeActivity = FaultAndCancellationHandlingFilter.GetFaultHandlers(context.Activity)
                        If (Not faultHandlers Is Nothing) Then
                            faultHandlers.RegisterForStatusChange(Activity.ClosedEvent, Me)
                            context.ExecuteActivity(faultHandlers)
                        ElseIf Not CompensationUtils.TryCompensateLastCompletedChildActivity(context, context.Activity, Me) Then
                            Me.SafeReleaseLockOnStatusChange(context)
                        End If
                    End If
                End If
            ElseIf ((TypeOf e.Activity Is FaultHandlersActivity OrElse TypeOf e.Activity Is CancellationHandlerActivity) AndAlso (e.ExecutionStatus = ActivityExecutionStatus.Closed)) Then
                e.Activity.UnregisterForStatusChange(Activity.ClosedEvent, Me)
                If (context.Activity.GetValue(ActivityExecutionContext.CurrentExceptionProperty) Is Nothing) Then
                    Me.SafeReleaseLockOnStatusChange(context)
                ElseIf Not CompensationUtils.TryCompensateLastCompletedChildActivity(context, context.Activity, Me) Then
                    Me.SafeReleaseLockOnStatusChange(context)
                End If
            ElseIf (e.ExecutionStatus = ActivityExecutionStatus.Closed) Then
                e.Activity.UnregisterForStatusChange(Activity.ClosedEvent, Me)
                If Not CompensationUtils.TryCompensateLastCompletedChildActivity(context, context.Activity, Me) Then
                    Me.SafeReleaseLockOnStatusChange(context)
                End If
            End If
        End Sub

        Private Sub SafeReleaseLockOnStatusChange(ByVal context As ActivityExecutionContext)
            Try 
                context.Activity.ReleaseLockOnStatusChange(Me)
            Catch exception1 As Exception
                context.Activity.RemoveProperty(FaultAndCancellationHandlingFilter.FaultProcessedProperty)
                Throw
            End Try
        End Sub


        ' Fields
        Public Shared FaultProcessedProperty As DependencyProperty = DependencyProperty.RegisterAttached("FaultProcessed", GetType(Boolean), GetType(FaultAndCancellationHandlingFilter), New PropertyMetadata(False))
    End Class
End Namespace

